home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / termlib.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  16KB  |  666 lines

  1. /* vi:set ts=4 sw=4:
  2.    The following software is (C) 1984 Peter da Silva,
  3.    the Mad Australian, in the public domain. It may
  4.    be re-distributed for any purpose with the inclusion
  5.    of this notice. */
  6.  
  7. /* modified by Bram Moolenaar for use with VIM - Vi Improved */
  8. /* a few bugs removed by Olaf 'Rhialto' Seibert */
  9.  
  10. /* TERMLIB: Terminal independant database. */
  11.  
  12. #include "vim.h"
  13. #include "proto.h"
  14. #include "proto/termlib.pro"
  15.  
  16. #if !defined(AMIGA) && !defined(VMS)
  17. # include <sgtty.h>
  18. #endif
  19.  
  20. static int    getent __PARMS((char *, char *, FILE *, int));
  21. static int    nextent __PARMS((char *, FILE *, int));
  22. static int    _match __PARMS((char *, char *));
  23. static char    *_addfmt __PARMS((char *, char *, int));
  24. static char    *_find __PARMS((char *, char *));
  25.  
  26. /*
  27.  * Global variables for termlib
  28.  */
  29.  
  30. char    *tent;                /* Pointer to terminal entry, set by tgetent */
  31. char    PC = 0;               /* Pad character, default NULL */
  32. char    *UP = 0, *BC = 0;     /* Pointers to UP and BC strings from database */
  33. short    ospeed;               /* Baud rate (1-16, 1=300, 16=19200), as in stty */
  34.  
  35. /*
  36.  * Module: tgetent
  37.  *
  38.  * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
  39.  *
  40.  * Calling conventions: char tbuf[TBUFSZ+], term=canonical name for
  41.  *            terminal.
  42.  *
  43.  * Returned values: 1 = success, -1 = can't open file,
  44.  *            0 = can't find terminal.
  45.  *
  46.  * Notes
  47.  *        Should probably supply static buffer.
  48.  *
  49.  *        Uses environment variables "TERM" and
  50.  *    "TERMCAP". If TERM = term (that is, if the argument
  51.  *    matches the environment) then it looks at TERMCAP.
  52.  *        If TERMCAP begins with a slash, then it assumes
  53.  *    this is the file to search rather than /etc/termcap.
  54.  *        If TERMCAP does not begin with a slash, and it
  55.  *    matches TERM, then this is used as the entry.
  56.  *
  57.  *        This could be simplified considerably for non-UNIX
  58.  *    systems.
  59.  */
  60.  
  61. #ifdef AMIGA
  62. # define TERMCAPFILE "s:termcap"
  63. #else
  64. # define TERMCAPFILE "/etc/termcap"
  65. #endif
  66.  
  67. tgetent(tbuf, term)
  68.     char    *tbuf;               /* Buffer to hold termcap entry, TBUFSZ bytes max */
  69.     char    *term;               /* Name of terminal */
  70. {
  71.     char    tcbuf[32];           /* Temp buffer to handle */
  72.     char    *tcptr = tcbuf;      /* extended entries */
  73.     char    *tcap = TERMCAPFILE; /* Default termcap file */
  74.     char    *tmp;
  75.     FILE    *termcap;
  76.     int        retval = 0;
  77.     int        len;
  78.  
  79.     if ((tmp = (char *)vim_getenv((char_u *)"TERMCAP")) != NULL)
  80.     {
  81.         if (*tmp == '/')            /* TERMCAP = name of termcap file */
  82.         {
  83.             tcap = tmp ;
  84. #if defined(AMIGA)
  85.             /* Convert /usr/share/lib/termcap to usr:share/lib/termcap */
  86.             tcap++;
  87.             tmp = strchr(tcap, '/');
  88.             if (tmp)
  89.                 *tmp = ':';
  90. #endif
  91.         }
  92.         else                        /* TERMCAP = termcap entry itself */
  93.         {
  94.             int tlen = strlen(term);
  95.  
  96.             while (*tmp && *tmp != ':') /* Check if TERM matches */
  97.             {
  98.                 char *nexttmp;
  99.  
  100.                 while (*tmp == '|')
  101.                     tmp++;
  102.                 nexttmp  = _find(tmp, ":|");    /* Rhialto */
  103.                 if (tmp+tlen == nexttmp && _match(tmp, term) == tlen)
  104.                 {
  105.                     strcpy(tbuf, tmp);
  106.                     tent = tbuf;
  107.                     return 1;
  108.                 } 
  109.                 else
  110.                     tmp = nexttmp;
  111.             }
  112.         }
  113.     }
  114.     if (!(termcap = fopen(tcap, "r")))
  115.     {
  116.         strcpy(tbuf, tcap);
  117.         return -1;
  118.     }
  119.  
  120.     len = 0;
  121.     while (getent(tbuf + len, term, termcap, TBUFSZ - len))
  122.     {
  123.         tcptr = tcbuf;        /* Rhialto */
  124.         if ((term = tgetstr("tc", &tcptr)))         /* extended entry */
  125.         {
  126.             rewind(termcap);
  127.             len = strlen(tbuf);
  128.         }
  129.         else
  130.         {
  131.             retval = 1; 
  132.             tent = tbuf;        /* reset it back to the beginning */
  133.             break;
  134.         }
  135.     }
  136.     fclose(termcap);
  137.     return retval;
  138. }
  139.  
  140.     static int
  141. getent(tbuf, term, termcap, buflen)
  142.     char    *tbuf, *term;
  143.     FILE    *termcap;
  144.     int        buflen;
  145. {
  146.     char    *tptr;
  147.     int        tlen = strlen(term);
  148.  
  149.     while (nextent(tbuf, termcap, buflen))   /* For each possible entry */
  150.     {
  151.         tptr = tbuf;
  152.         while (*tptr && *tptr != ':')    /* : terminates name field */
  153.         {
  154.             char    *nexttptr;
  155.  
  156.             while (*tptr == '|')             /* | seperates names */
  157.                 tptr++;
  158.             nexttptr = _find(tptr, ":|");     /* Rhialto */
  159.             if (tptr + tlen == nexttptr &&
  160.                 _match(tptr, term) == tlen)             /* FOUND! */
  161.             {
  162.                 tent = tbuf;
  163.                 return 1;
  164.             } 
  165.             else                           /* Look for next name */
  166.                 tptr = nexttptr;
  167.         }
  168.     }
  169.     return 0;
  170. }
  171.  
  172.     static int
  173. nextent(tbuf, termcap, buflen)         /* Read 1 entry from TERMCAP file */
  174.     char    *tbuf;
  175.     FILE    *termcap;
  176.     int        buflen;
  177. {
  178.     char *lbuf = tbuf;           /* lbuf=line buffer */
  179.                                  /* read lines straight into buffer */
  180.  
  181.     while (lbuf < tbuf+buflen &&                        /* There's room and */
  182.           fgets(lbuf, (int)(tbuf+buflen-lbuf), termcap))        /* another line */
  183.     {
  184.         int llen = strlen(lbuf);
  185.  
  186.         if (*lbuf == '#')                               /* eat comments */
  187.             continue;
  188.         if (lbuf[-1] == ':' &&                        /* and whitespace */
  189.             lbuf[0] == '\t' &&
  190.             lbuf[1] == ':')
  191.         {
  192.             strcpy(lbuf, lbuf+2);
  193.             llen -= 2;
  194.         }
  195.         if (lbuf[llen-2] == '\\')                  /* and continuations */
  196.             lbuf += llen-2;
  197.         else
  198.         {
  199.             lbuf[llen-1]=0;           /* no continuation, return */
  200.             return 1;
  201.         }
  202.     }
  203.  
  204.     return 0;                                    /* ran into end of file */
  205. }
  206.  
  207. /*
  208.  * Module: tgetflag
  209.  *
  210.  * Purpose: returns flag true or false as to the existence of a given
  211.  *        entry. used with 'bs', 'am', etc...
  212.  *
  213.  * Calling conventions: id is the 2 character capability id.
  214.  *
  215.  * Returned values: 1 for success, 0 for failure.
  216.  */
  217.  
  218. tgetflag(id)
  219.     char *id;
  220. {
  221.     char    buf[256], *ptr = buf;
  222.  
  223.     return tgetstr(id, &ptr) ? 1 : 0;
  224. }
  225.  
  226. /*
  227.  * Module: tgetnum
  228.  *
  229.  * Purpose: get numeric value such as 'li' or 'co' from termcap.
  230.  *
  231.  * Calling conventions: id = 2 character id.
  232.  *
  233.  * Returned values: -1 for failure, else numerical value.
  234.  */
  235.  
  236. tgetnum(id)
  237. char *id;
  238. {
  239.     char *ptr, buf[256];
  240.     ptr = buf;
  241.  
  242.     if (tgetstr(id, &ptr))
  243.         return atoi(buf);
  244.     else
  245.         return 0;
  246. }
  247.  
  248. /*
  249.  * Module: tgetstr
  250.  *
  251.  * Purpose: get terminal capability string from database.
  252.  *
  253.  * Calling conventions: id is the two character capability id.
  254.  *            (*buf) points into a hold buffer for the
  255.  *            id. the capability is copied into the buffer
  256.  *            and (*buf) is advanced to point to the next
  257.  *            free byte in the buffer.
  258.  *
  259.  * Returned values: 0 = no such entry, otherwise returns original
  260.  *            (*buf) (now a pointer to the string).
  261.  *
  262.  * Notes
  263.  *        It also decodes certain escape sequences in the buffer.
  264.  *    they should be obvious from the code:
  265.  *        \E = escape.
  266.  *        \n, \r, \t, \f, \b match the 'c' escapes.
  267.  *        ^x matches control-x (^@...^_).
  268.  *        \nnn matches nnn octal.
  269.  *        \x, where x is anything else, matches x. I differ
  270.  *    from the standard library here, in that I allow ^: to match
  271.  *    :.
  272.  *
  273.  */
  274.  
  275. char *
  276. tgetstr(id, buf)
  277. char    *id, **buf;
  278. {
  279.     int    len = strlen(id);
  280.     char *tmp=tent;
  281.     char *hold;
  282.     int        i;
  283.  
  284.     do {
  285.         tmp = _find(tmp, ":");                     /* For each field */
  286.         while (*tmp == ':')                        /* skip empty fields */
  287.             tmp++;
  288.         if (!*tmp)
  289.             break;
  290.  
  291.         if (_match(id, tmp) == len) {
  292.             tmp += len;                   /* find '=' '@' or '#' */
  293.             if (*tmp == '@')                  /* :xx@: entry for tc */
  294.                 return 0;                   /* deleted entry */
  295.             hold= *buf;
  296.             while (*++tmp && *tmp != ':') {/* not at end of field */
  297.                 switch(*tmp) {
  298.                 case '\\':            /* Expand escapes here */
  299.                     switch(*++tmp) {
  300.                     case 0:        /* ignore backslashes */
  301.                         tmp--;    /* at end of entry */
  302.                         break;   /* shouldn't happen */
  303.                     case 'e':
  304.                     case 'E':                     /* ESC */
  305.                         *(*buf)++ = '\033'; 
  306.                         break;
  307.                     case 'n':                      /* \n */
  308.                         *(*buf)++ = '\n'; 
  309.                         break;
  310.                     case 'r':                      /* \r */
  311.                         *(*buf)++ = '\r'; 
  312.                         break;
  313.                     case 't':                      /* \t */
  314.                         *(*buf)++ = '\t'; 
  315.                         break;
  316.                     case 'b':                      /* \b */
  317.                         *(*buf)++ = '\b'; 
  318.                         break;
  319.                     case 'f':                      /* \f */
  320.                         *(*buf)++ = '\f'; 
  321.                         break;
  322.                     case '0':                    /* \nnn */
  323.                     case '1': 
  324.                     case '2': 
  325.                     case '3': 
  326.                     case '4':
  327.                     case '5': 
  328.                     case '6': 
  329.                     case '7': 
  330.                     case '8': 
  331.                     case '9':
  332.                         **buf = 0;
  333.                             /* get up to three digits */
  334.                         for (i = 0; i < 3 && isdigit(*tmp); ++i)
  335.                             **buf = **buf * 8 + *tmp++ - '0';
  336.                         (*buf)++;
  337.                         tmp--;
  338.                         break;
  339.                     default:      /* \x, for all other x */
  340.                         *(*buf)++= *tmp;
  341.                     }
  342.                     break;
  343.                 case '^':              /* control characters */
  344.                     *(*buf)++ = *++tmp - '@'; 
  345.                     break;
  346.                 default: 
  347.                     *(*buf)++ = *tmp;
  348.                 }
  349.             }
  350.             *(*buf)++ = 0;
  351.             return hold;
  352.         }
  353.     } while (*tmp);
  354.  
  355.     return 0;
  356. }
  357.  
  358. /*
  359.  * Module: tgoto
  360.  *
  361.  * Purpose: decode cm cursor motion string.
  362.  *
  363.  * Calling conventions: cm is cursor motion string.
  364.  *            line, col, are the desired destination.
  365.  *
  366.  * Returned values: a string pointing to the decoded string, or
  367.  *            "OOPS" if it cannot be decoded.
  368.  *
  369.  * Notes
  370.  *        The accepted escapes are:
  371.  *            %d     as in printf, 0 origin.
  372.  *            %2, %3     like %02d, %03d in printf.
  373.  *            %.     like %c
  374.  *            %+x     adds <x> to value, then %.
  375.  *            %>xy     if value>x, adds y. No output.
  376.  *            %i     increments line& col, no output.
  377.  *            %r     reverses order of line&col. No output.
  378.  *            %%     prints as a single %.
  379.  *            %n     exclusive or row & col with 0140.
  380.  *            %B     BCD, no output.
  381.  *            %D     reverse coding (x-2*(x%16)), no output.
  382.  */
  383.  
  384. char *
  385. tgoto(cm, col, line)
  386. char    *cm;                                      /* cm string, from termcap */
  387. int    col,                                           /* column, x position */
  388.     line;                                            /* line, y position */
  389. {
  390.     char    gx, gy,                                           /*    x, y */
  391.         *ptr,                                     /* pointer in 'cm' */
  392.         reverse = 0,                                 /* reverse flag */
  393.         *bufp,                         /* pointer in returned string */
  394.         addup = 0,                                     /* add upline */
  395.         addbak = 0,                                    /* add backup */
  396.         c;
  397.     static char buffer[32];
  398.  
  399.     if (!cm)
  400.         return "OOPS";                       /* Kludge, but standard */
  401.  
  402.     bufp = buffer;
  403.     ptr = cm;
  404.  
  405.     while (*ptr) {
  406.         if ((c = *ptr++) != '%') {                     /* normal char */
  407.             *bufp++ = c;
  408.         } else {                                         /* % escape */
  409.             switch(c = *ptr++) {
  410.             case 'd':                                 /* decimal */
  411.                 bufp = _addfmt(bufp, "%d", line);
  412.                 line = col;
  413.                 break;
  414.             case '2':                         /* 2 digit decimal */
  415.                 bufp = _addfmt(bufp, "%02d", line);
  416.                 line = col;
  417.                 break;
  418.             case '3':                         /* 3 digit decimal */
  419.                 bufp = _addfmt(bufp, "%03d", line);
  420.                 line = col;
  421.                 break;
  422.             case '>':                      /* %>xy: if >x, add y */
  423.                 gx = *ptr++;
  424.                 gy = *ptr++;
  425.                 if (col>gx) col += gy;
  426.                 if (line>gx) line += gy;
  427.                 break;
  428.             case '+':                              /* %+c: add c */
  429.                 line += *ptr++;
  430.             case '.':                               /* print x/y */
  431.                 if (line == '\t' ||                /* these are */
  432.                    line == '\n' ||             /* chars that */
  433.                    line == '\004' ||             /* UNIX hates */
  434.                    line == '\0') {
  435.                     line++;         /* so go to next pos */
  436.                     if (reverse == (line == col))
  437.                         addup=1;      /* and mark UP */
  438.                     else
  439.                         addbak=1;           /* or BC */
  440.                 }
  441.                 *bufp++=line;
  442.                 line = col;
  443.                 break;
  444.             case 'r':                              /* r: reverse */
  445.                 gx = line; 
  446.                 line = col; 
  447.                 col = gx;
  448.                 reverse = 1;
  449.                 break;
  450.             case 'i':             /* increment (1-origin screen) */
  451.                 col++;
  452.                 line++;
  453.                 break;
  454.             case '%':                          /* %%=% literally */
  455.                 *bufp++='%';
  456.                 break;
  457.             case 'n':                       /* magic DM2500 code */
  458.                 line ^= 0140;
  459.                 col ^= 0140;
  460.                 break;
  461.             case 'B':                            /* bcd encoding */
  462.                 line = line/10<<4+line%10;
  463.                 col = col/10<<4+col%10;
  464.                 break;
  465.             case 'D':                   /* magic Delta Data code */
  466.                 line = line-2*(line&15);
  467.                 col = col-2*(col&15);
  468.                 break;
  469.             default:                           /* Unknown escape */
  470.                 return "OOPS";
  471.             }
  472.         }
  473.     }
  474.  
  475.     if (addup)                                              /* add upline */
  476.         if (UP) {
  477.             ptr=UP;
  478.             while (isdigit(*ptr) || *ptr == '.')
  479.                 ptr++;
  480.             if (*ptr == '*')
  481.                 ptr++;
  482.             while (*ptr)
  483.                 *bufp++ = *ptr++;
  484.         }
  485.  
  486.     if (addbak)                                          /* add backspace */
  487.         if (BC) {
  488.             ptr=BC;
  489.             while (isdigit(*ptr) || *ptr == '.')
  490.                 ptr++;
  491.             if (*ptr == '*')
  492.                 ptr++;
  493.             while (*ptr)
  494.                 *bufp++ = *ptr++;
  495.         } 
  496.         else
  497.             *bufp++='\b';
  498.  
  499.     *bufp = 0;
  500.  
  501.     return(buffer);
  502. }
  503.  
  504. /*
  505.  * Module: tinit
  506.  *
  507.  * Purpose: simplified terminal initialisation.
  508.  *
  509.  * Calling conventions: name is name of terminal.
  510.  *
  511.  * Returned values: none.
  512.  *
  513.  * Notes
  514.  *        tinit calls tgetent, then sets up the global
  515.  *    variables PC, UP, BC, ospeed appropriately.
  516.  *
  517.  */
  518.  
  519. #if 0        /* already included in term.c */
  520.  
  521. char tbuf[TBUFSZ];                                /* Buffer for termcap entry */
  522. char junkbuf[TBUFSZ];                                  /* Big buffer for junk */
  523. char *junkptr;
  524.  
  525. tinit(name)
  526. char *name;
  527. {
  528. #ifndef AMIGA
  529.     struct sgttyb sgbuf;
  530. #endif
  531.     char *ps;
  532.  
  533.     junkptr = junkbuf;
  534.  
  535.     tgetent(tbuf, name);
  536.  
  537.     ps = tgetstr("pc", &junkptr);
  538.     if (ps) PC = *ps;
  539.     UP = tgetstr("up", &junkptr);
  540.     BC = tgetstr("bc", &junkptr);
  541.  
  542. #ifdef AMIGA
  543.     ospeed=0;
  544. #else
  545.     gtty(1, &sgbuf);
  546.     ospeed=sgbuf.sg_ospeed;
  547. #endif
  548.     return 0;
  549. }
  550. #endif
  551.  
  552. /*
  553.  * Module: tputs
  554.  *
  555.  * Purpose: decode padding information
  556.  *
  557.  * Calling conventions: cp = string to be padded, affcnt = # of items
  558.  *            affected (lines, characters, whatever),
  559.  *            outc = routine to output 1 character.
  560.  *
  561.  * Returned values: none
  562.  *
  563.  * Notes
  564.  *        cp has padding information ahead of it, in the form
  565.  *    nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
  566.  *    and may be a decimal (nnn.mmm). If the asterisk is given, then
  567.  *    the delay is multiplied by afcnt. The delay is produced by outputting
  568.  *    a number of nulls (or other padding char) after printing the
  569.  *    TEXT.
  570.  *
  571.  */
  572.  
  573. long _bauds[16]={
  574.     0,    50,    75,    110,
  575.     134,    150,    200,    300,
  576.     600,    1200,    1800,    2400,
  577.     4800,    9600,    19200,    19200 };
  578.  
  579. tputs(cp, affcnt, outc)
  580. char *cp;                                                 /* string to print */
  581. int affcnt;                                      /* Number of lines affected */
  582. void (*outc) __ARGS((unsigned int));                              /* routine to output 1 character */
  583. {
  584.     long    frac,                    /* 10^(#digits after decimal point) */
  585.         counter,                                           /* digits */
  586.         atol __ARGS((const char *));
  587.  
  588.     if (isdigit(*cp)) {
  589.         counter = 0;
  590.         frac = 1000;
  591.         while (isdigit(*cp))
  592.             counter = counter * 10L + (long)(*cp++ - '0');
  593.         if (*cp == '.')
  594.             while (isdigit(*++cp)) {
  595.                 counter = counter * 10L + (long)(*cp++ - '0');
  596.                 frac = frac * 10;
  597.             }
  598.         if (*cp!='*') {                 /* multiply by affected lines */
  599.             if (affcnt>1) affcnt = 1;
  600.         } 
  601.         else
  602.             cp++;
  603.  
  604.         /* Calculate number of characters for padding counter/frac ms delay */
  605.         if (ospeed)
  606.             counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
  607.  
  608.         while (*cp)                                  /* output string */
  609.             (*outc)(*cp++);
  610.         if (ospeed)
  611.             while (counter--)            /* followed by pad characters */
  612.                 (*outc)(PC);
  613.     } 
  614.     else
  615.         while (*cp)
  616.             (*outc)(*cp++);
  617.     return 0;
  618. }
  619.  
  620. /*
  621.  * Module: tutil.c
  622.  *
  623.  * Purpose: Utility routines for TERMLIB functions.
  624.  *
  625.  */
  626.  
  627.     static int
  628. _match(s1, s2)                 /* returns length of text common to s1 and s2 */
  629. char *s1, *s2;
  630. {
  631.     int i = 0;
  632.  
  633.     while (s1[i] && s1[i] == s2[i])
  634.         i++;
  635.  
  636.     return i;
  637. }
  638.  
  639.     static char *
  640. _find(s, set)   /* finds next c in s that's a member of set, returns pointer */
  641. char *s, *set;
  642. {
  643.     for(; *s; s++) {
  644.         char    *ptr = set;
  645.  
  646.         while (*ptr && *s != *ptr)
  647.             ptr++;
  648.  
  649.         if (*ptr)
  650.             return s;
  651.     }
  652.  
  653.     return s;
  654. }
  655.  
  656.     static char *
  657. _addfmt(buf, fmt, val)             /* add val to buf according to format fmt */
  658. char *buf, *fmt;
  659. int val;
  660. {
  661.     sprintf(buf, fmt, val);
  662.     while (*buf)
  663.         buf++;
  664.     return buf;
  665. }
  666.